﻿using Microsoft.CSharp;
using System;
using System.Collections;
using System.Data;
using System.Runtime.InteropServices;
using System.Threading;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.ComponentModel;
using System.Dynamic;
using System.Reflection;
using System.Linq;
using System.CodeDom.Compiler;
using System.Security.Permissions;
using System.Security;
using System.Security.Policy;
using System.Diagnostics;
using System.Text;

namespace GT
{
    public static partial class App
    {
        public static string Path
        {
            get
            {
                Uri ret = new Uri(System.Reflection.Assembly.GetEntryAssembly().GetName().CodeBase);
                return System.IO.Path.GetDirectoryName(ret.LocalPath);
            }
        }

        public static string GetPath(string fileName = "")
        {
            return System.IO.Path.Combine(Path, fileName);
        }
    }

    public static partial class Functions
    {
        internal class CompilerSetup
        {
            public CSharpCodeProvider CodeProvider = new CSharpCodeProvider(new Dictionary<String, String> { { "CompilerVersion", "v4.0" } });
            public CompilerParameters Parameters = new CompilerParameters();
        }

        public static CompilerResults CompileSource(string sourceText, string targetType = "library")
        {
            return CompileSource(sourceText.ToArrayOfOne(), targetType);
        }

        public static CompilerResults CompileSource(string[] sourceText, string targetType = "library")
        {
            CompilerSetup cs = InitCompiler(targetType);
            return cs.CodeProvider.CompileAssemblyFromSource(cs.Parameters, sourceText);   
        }

        public static CompilerResults CompileFile(string sourceFile, string targetType = "library")
        {
            return CompileFile(sourceFile.ToArrayOfOne(), targetType);
        }

        public static CompilerResults CompileFile(string[] sourceFiles, string targetType = "library")
        {
            CompilerSetup cs = InitCompiler(targetType);
            return cs.CodeProvider.CompileAssemblyFromFile(cs.Parameters, sourceFiles);
        }

        public static object CompileTypeSource(string sourceText, string typeName)
        {
            return CompileTypeSource(sourceText.ToArrayOfOne(), typeName);
        }

        public static object CompileTypeSource(string[] sourceText, string typeName)
        {
            var cr = CompileSource(sourceText);
            if (cr.Errors.Count > 0)
                throw new CompilationException(cr.Errors);

            var asm = cr.CompiledAssembly;
            return cr.CompiledAssembly.CreateInstance(typeName);
        }

        public static object CompileTypeFile(string sourceFile, string typeName)
        {
            return CompileTypeFile(sourceFile.ToArrayOfOne(), typeName);
        }

        public static object CompileTypeFile(string[] sourceFile, string typeName)
        {
            var cr = CompileFile(sourceFile);
            if (cr.Errors.Count > 0)
                throw new CompilationException(cr.Errors);

            var asm = cr.CompiledAssembly;
            return cr.CompiledAssembly.CreateInstance(typeName);
        }

//        // See: http://msdn.microsoft.com/en-us/library/bb763046.aspx
//        public static object CompileSandboxedType(string sourceText, string typeName, SecurityPermission[] permissions)
//        {
//            PermissionSet permSet = new PermissionSet(PermissionState.None);
//            permissions.ForEach(p => permSet.AddPermission(p));
//            return CompileSandboxedType(sourceText, typeName, permSet);
//        }

//        // See: http://msdn.microsoft.com/en-us/library/bb763046.aspx
//        public static object CompileSandboxedType(string sourceText, string typeName, SecurityZone secZone = SecurityZone.Internet)
//        {
//            Evidence ev = new Evidence();
//            ev.AddHostEvidence(new Zone(secZone));
//            return CompileSandboxedType(sourceText, typeName, SecurityManager.GetStandardSandbox(ev));
//        }

//        // See: http://msdn.microsoft.com/en-us/library/bb763046.aspx
//        private static object CompileSandboxedType(string sourceText, string typeName, PermissionSet permissionSet)
//        {
//            CompilerSetup cs = InitCompiler("library");
//            const string InitCode = @"
//using System;
//using System.Security;
//
//[assembly:System.Security.SecurityTransparent]
//namespace __gtcompiledcode__
//{
//    public class AssemblySetup
//    {
//        public object ScriptObject { get; private set; }
//        public Exception CreationException { get; private set; }
//
//        public AssemblySetup()
//        {
//            ScriptObject = null;
//            CreationException = null;
//        }
//        
//        public bool InitScript(PermissionSet permissions, string typeName)
//        {
//            //try
//            //{
//                Type type = Type.GetType(typeName);
//                permissions.PermitOnly();
//                ScriptObject = Activator.CreateInstance(type);
//                return true;
//            //}
//            //catch (Exception ex)
//            //{
//            //}
//        }
//    }
//}";

//            CompilerResults results = cs.CodeProvider.CompileAssemblyFromSource(cs.Parameters, InitCode, sourceText);
//            if (results.Errors.Count > 0)
//                throw new CompilationException(results.Errors);

//            var asm = results.CompiledAssembly;
//            dynamic AssemblySetup = asm.CreateInstance("__gtcompiledcode__.AssemblySetup");
//            AssemblySetup.InitScript(permissionSet, typeName);
//            return AssemblySetup.ScriptObject;
//        }

        private static CompilerSetup InitCompiler(string targetType)
        {
            CSharpCodeProvider csp = new CSharpCodeProvider(new Dictionary<String, String> { { "CompilerVersion", "v4.0" } });
            CompilerParameters cp = new CompilerParameters();

            System.Reflection.Assembly.GetEntryAssembly().GetReferencedAssembliesPaths().ForEach(i => cp.ReferencedAssemblies.Add(i));

            cp.WarningLevel = 3;

            cp.CompilerOptions = "/target:" + targetType + " /optimize+";
            cp.GenerateExecutable = false;
            cp.GenerateInMemory = true;

            return new CompilerSetup() { CodeProvider = csp, Parameters = cp };
        }

        public static int GetPercentage(int totalItems, int count)
        {
            if (totalItems == 0) return 0;
            return (int)((count * 100) * (1.0 / totalItems));
        }

        public static int GetRandomInt()
        {
            return GetRandomInt(int.MinValue, int.MaxValue);
        }

        public static int GetRandomInt(int max)
        {
            return GetRandomInt(int.MinValue, max);
        }

        public static int GetRandomInt(int min, int max)
        {
            return new Random(GetRandomSeed()).Next(min, max);
        }

        internal static int GetRandomSeed()
        {
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            byte[] buffer = new byte[4];

            rng.GetBytes(buffer);
            int result = BitConverter.ToInt32(buffer, 0);
            return result;
        }

        public static void RaiseEvent(Delegate theEvent, object[] args)
        {
            foreach (Delegate d in theEvent.GetInvocationList())
            {
                ISynchronizeInvoke syncer = d.Target as ISynchronizeInvoke;
                if (syncer == null)
                {
                    d.DynamicInvoke(args);
                }
                else
                {
                    syncer.BeginInvoke(d, args);  // cleanup omitted
                }
            }
        }

        public static Thread InvokeInThread(object arg, Action<object> action)
        {
            Thread ret = new Thread(a =>
                {
                    try
                    {
                        action(a);
                    }
                    // Elevate any errors in case the calling code
                    // doesn't implement Application.ThreadException
                    catch { throw; }
                });
            ret.Start(arg);
            return ret;
        }

        public static Thread InvokeInThread(Action action)
        {
            Thread ret = new Thread(() =>
                {
                    try
                    {
                        action();
                    }
                    // Elevate any errors in case the calling code
                    // doesn't implement Application.ThreadException
                    catch { throw; }
                });
            ret.Start();
            return ret;
        }
    }

    public class Comparer<T> : IEqualityComparer<T>
    {
        private readonly Func<T, T, bool> _lambdaComparer;
        private readonly Func<T, int> _lambdaHash;

        public Comparer(Func<T, T, bool> comparer) :
            this(comparer, o => 0)
        {
        }

        public Comparer(Func<T, T, bool> comparer, Func<T, int> hash)
        {
            if (comparer == null)
                throw new ArgumentNullException("comparer");
            if (hash == null)
                throw new ArgumentNullException("hash");

            _lambdaComparer = comparer;
            _lambdaHash = hash;
        }

        public bool Equals(T x, T y)
        {
            return _lambdaComparer(x, y);
        }

        public int GetHashCode(T obj)
        {
            return _lambdaHash(obj);
        }
    }

    public class DynamicProxy : DynamicObject, INotifyPropertyChanged 
    { 
        #region protected methods 
        protected PropertyInfo GetPropertyInfo(string propertyName) 
        { 
            return Object.GetType().GetProperties().First(propertyInfo => propertyInfo.Name == propertyName); 
        } 
	
        protected virtual void SetMember(string propertyName, object value) 
        { 
            GetPropertyInfo(propertyName).SetValue(Object, value, null); 
            RaisePropertyChanged(propertyName); 
        } 
	
        protected virtual object GetMember(string propertyName)
        { 
            return GetPropertyInfo(propertyName).GetValue(Object, null);
        } 
	
        protected virtual void OnPropertyChanged(string propertyName) 
        { 
            //if (PropertyChanged != null) 
            //    PropertyChanged(Object, new PropertyChangedEventArgs(propertyName));
            PropertyChanged.Raise(new object[] { Object, new PropertyChangedEventArgs(propertyName) });
        } 
	
        protected virtual void RaisePropertyChanged(string propertyName) 
        { 
            OnPropertyChanged(propertyName);
        } 
        #endregion 
	
        #region constructor 
        public DynamicProxy() { } 
        public DynamicProxy(object proxiedObject) 
        { 
            Object = proxiedObject; 
        } 
        #endregion 
	
        public override bool TryConvert(ConvertBinder binder, out object result) 
        { 
            if (binder.Type == typeof(INotifyPropertyChanged))
            { 
                result = this; 
                return true; 
            } 
		
            if (Object != null && binder.Type.IsAssignableFrom(Object.GetType()))
            { 
                result = Object; 
                return true; 
            } 
            else 
                return base.TryConvert(binder, out result); 
        } 
	
        public override bool TryGetMember(GetMemberBinder binder, out object result) 
        { 
            result = GetMember(binder.Name); 
            return true;
        } 
	
        public override bool TrySetMember(SetMemberBinder binder, object value) 
        { 
            SetMember(binder.Name, value); 
            return true; 
        } 
  
        #region public properties 
        public object Object { get; set; } 
        #endregion 
	
        #region INotifyPropertyChanged Member 
        public event PropertyChangedEventHandler PropertyChanged; 
        #endregion
    }

    public class CompilationException : Exception
    {
        public CompilationException(CompilerErrorCollection compilerErrorCollection)
        {
            CompilerErrors = compilerErrorCollection;
        }

        public CompilerErrorCollection CompilerErrors { get; private set; }
        public override string ToString()
        {
            return CompilerErrors.OfType<CompilerError>().Select(i => i.ToString()).Join(Environment.NewLine);
        }
    }

    public class ToStringWrapper<T>
    {
        private Func<string> customToString;
        private T _object;

        public ToStringWrapper(T obj, Func<string> toStringFunc)
        {
            _object = obj;
            customToString = toStringFunc;
        }

        public override string ToString()
        {
            return customToString();
        }

        public static implicit operator T(ToStringWrapper<T> wrapper)
        {
            return wrapper._object;
        }

        public T Value { get { return _object; } }
    }

    public static partial class Extensions
    {
        [DllImport("urlmon.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false)]
        private static extern int FindMimeFromData(IntPtr pBC,
            [MarshalAs(UnmanagedType.LPWStr)]
            string pwzUrl,
            [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1, SizeParamIndex = 3)]
            byte[] pBuffer,
            int cbSize,
            [MarshalAs(UnmanagedType.LPWStr)]
            string pwzMimeProposed,
            int dwMimeFlags,
            out IntPtr ppwzMimeOut,
            int dwReserved);


        
        #region Helper methods

        private static Object[] GetRowFields(DataRow dr, String[] arrFieldNames)
        {
            if (arrFieldNames.Length == 1)
                return new Object[] { dr[arrFieldNames[0]] };
            else
            {
                ArrayList itemArray = new ArrayList();
                foreach (String field in arrFieldNames)
                    itemArray.Add(dr[field]);

                return itemArray.ToArray();
            }
        }

        private static Boolean ObjectComparison(Object a, Object b)
        {
            if (a == DBNull.Value && b == DBNull.Value) //  both are DBNull.Value
                return true;
            if (a == DBNull.Value || b == DBNull.Value) //  only one is DBNull.Value
                return false;
            return (a.Equals(b));  // value type standard comparison
        }

        private static Boolean ObjectComparison(Object[] a, Object[] b)
        {
            Boolean retValue = true;
            Boolean singleCheck = false;

            if (a.Length == b.Length)
                for (Int32 i = 0; i < a.Length; i++)
                {
                    if (!(singleCheck = ObjectComparison(a[i], b[i])))
                    {
                        retValue = false;
                        break;
                    }
                    retValue = retValue && singleCheck;
                }

            return retValue;
        }

        #endregion
    }
}
